package com.qunchuang.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qunchuang.constant.LiftConstant;
import com.qunchuang.constant.LiftPointsConstant;
import com.qunchuang.constant.LiftStorageLocationConstant;
import com.qunchuang.constant.LiftTaskStateConstant;
import com.qunchuang.mapper.LiftAgvcTaskMapper;
import com.qunchuang.mapper.LiftStorageLocationMapper;
import com.qunchuang.mapper.PointsMapper;
import com.qunchuang.pojo.dto.AgvRequestLiftDto;
import com.qunchuang.mapper.LiftMapper;
import com.qunchuang.pojo.entity.LiftAgvcTaskEntity;
import com.qunchuang.pojo.entity.LiftEntity;
import com.qunchuang.pojo.entity.LiftStorageLocationEntity;
import com.qunchuang.pojo.entity.PointsEntity;
import com.qunchuang.pojo.vo.AssignmentLiftStorageLocationVo;
import com.qunchuang.service.LiftService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class LiftServiceImpl implements LiftService {
    @Autowired
    private LiftMapper liftMapper;
    @Autowired
    private LiftAgvcTaskMapper liftAgvcTaskMapper;
    @Autowired
    private PointsMapper pointsMapper;
    @Autowired
    private LiftStorageLocationMapper liftStorageLocationMapper;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public AssignmentLiftStorageLocationVo assignmentLiftStorageLocation(AgvRequestLiftDto agvRequestLiftDto) {
        AssignmentLiftStorageLocationVo assignmentLiftStorageLocationVo = new AssignmentLiftStorageLocationVo();
        //agv当前点位
        String currentPort = agvRequestLiftDto.getCurrentPort();
        //agvid
        String agvId = agvRequestLiftDto.getAgvId();
        //呼叫电梯起始楼层
        Integer startFloor = agvRequestLiftDto.getStartFloor();
        //电梯结束楼层
        Integer endFloor = agvRequestLiftDto.getEndFloor();
        synchronized (LiftServiceImpl.class) {
            //根据点位查询电梯
            QueryWrapper<PointsEntity> pointsEntityQueryWrapper = new QueryWrapper<>();
            pointsEntityQueryWrapper.eq("call_points",currentPort);
            pointsEntityQueryWrapper.eq("lift_healthy_state",LiftPointsConstant.LIFT_REQUEST_POINT_AVAILABLE);
            List<PointsEntity> pointsEntities = pointsMapper.selectList(pointsEntityQueryWrapper);
            List<String> collect = pointsEntities.stream().map(PointsEntity::getLiftId).collect(Collectors.toList());
            //查询电梯表
            QueryWrapper<LiftEntity> liftEntityQueryWrapper = new QueryWrapper<>();
            liftEntityQueryWrapper.in("lift_id",collect);
            liftEntityQueryWrapper.eq("lift_firefighting",LiftConstant.LIFT_FIREFIGHTING_SHUT_DOWN);
            List<LiftEntity> liftEntities = liftMapper.selectList(liftEntityQueryWrapper);
            List<LiftEntity> liftTask = liftEntities.stream().filter(e -> e.getLiftState().equals(1)).collect(Collectors.toList());
            List<LiftEntity> liftEmpty = liftEntities.stream().filter(e -> e.getLiftState().equals(0)).collect(Collectors.toList());
            log.info("查询电梯表电梯状态为0{}",liftEmpty);
            List<LiftEntity> liftExecute = liftEntities.stream().filter(e -> e.getLiftState().equals(2)).collect(Collectors.toList());
            log.info("查询电梯表电梯状态为2{}",liftExecute);
            //便利查询电梯状态为1，先进行撮合
            for (LiftEntity liftEntity : liftTask) {
                String liftId = liftEntity.getLiftId();
                //判断储位是否已满
                //long StorageLocationSize = selectBookingStorageLocationSize(liftId,LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE);
                Long StorageLocationSize = StorageLocationSize(liftId,  LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE);
                //电梯储位已满
                if (StorageLocationSize !=2){
                    //查询储位表
                    LiftStorageLocationEntity liftStorageLocationEntity = getLiftStorageLocation(liftId,  LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE);
                    String taskAgv = liftStorageLocationEntity.getAgvId();
                    //根据agv唯一来查询任务表
                    LiftAgvcTaskEntity liftAgvcTaskEntity = selectTaskFllor(taskAgv, liftId);
                    //判断起始结束楼层是否一致
                    Integer liftStartFloor = liftAgvcTaskEntity.getLiftStartFloor();
                    Integer liftEndFloor = liftAgvcTaskEntity.getLiftEndFloor();
                    //结束楼层一致
                    if (Objects.equals(startFloor, liftStartFloor) && Objects.equals(endFloor, liftEndFloor)){
                        //查询储位表，第二个储位
                        String storageLocationId = fetchStorageLocation(liftId,LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_FREE,LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_LOWEST_PRIORITY);
                        //分配储位
                        Integer i = updateLiftStorageLocation(agvId, storageLocationId);
                        ifUpdateSucceed(i,"修改电梯储位表成功，绑定储位","修改电梯储位表失败，进行报错回滚");
                        //创建任务
                        insertLiftAgvTask(liftId, agvId, storageLocationId, currentPort, startFloor, endFloor,LiftTaskStateConstant.LIFT_ASSIGNMENT_STORAGE_LOCATION,LiftTaskStateConstant.LIFT_STORAGE_LOCATION_FREE);
                        vo(assignmentLiftStorageLocationVo, liftId, storageLocationId);
                        return assignmentLiftStorageLocationVo;
                    }
                }
            }

            //便利查询电梯状态为0，直接分配电梯
            for (LiftEntity liftEntity : liftEmpty) {
                //查询离呼叫点位最近电梯
                LiftEntity liftNear = liftEmpty.stream().min(Comparator.comparing(s -> Math.abs(s.getLiftRemainFloor() - startFloor))).orElse(null);
                log.info("离呼叫点最近电梯：{}",liftNear);
                String liftId = liftNear.getLiftId();
                //查询电梯是否有预约，有预约不进行分配当前电梯
                QueryWrapper<LiftAgvcTaskEntity> liftAgvcTaskEntityQueryWrapper = new QueryWrapper<>();
                liftAgvcTaskEntityQueryWrapper.eq("lift_id",liftId);
                liftAgvcTaskEntityQueryWrapper.eq("booking",LiftTaskStateConstant.LIFT_STORAGE_LOCATION_RESERVED);
                Long selectBookingCount = liftAgvcTaskMapper.selectCount(liftAgvcTaskEntityQueryWrapper);
                if (selectBookingCount ==0){
                    //查询储位
                    String storageLocationId = fetchStorageLocation(liftId, LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_FREE, LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_HIGH_PRIORITY);
                    //分配储位
                    Integer i = updateLiftStorageLocation(agvId, storageLocationId);
                    ifUpdateSucceed(i,"修改电梯储位表成功,绑定储位","修改电梯储位表失败,进行报错回滚");
                    //创建任务
                    insertLiftAgvTask(liftId, agvId, storageLocationId, currentPort, startFloor, endFloor, LiftTaskStateConstant.LIFT_ASSIGNMENT_STORAGE_LOCATION, LiftTaskStateConstant.LIFT_STORAGE_LOCATION_FREE);
                    //修改电梯表
                    Integer i1 = updateListTask(liftId);
                    ifUpdateSucceed(i1,"修改电梯表成功,电梯状态改为1","修改电梯表失败，进行报错回滚");
                    //返回
                    vo(assignmentLiftStorageLocationVo, liftId, storageLocationId);
                    return assignmentLiftStorageLocationVo;
                }

            }
            for (LiftEntity liftEntity : liftExecute) {
                log.info("执行预约操作");
                //查询电梯储位是否预约已满
                String liftIds = liftEntity.getLiftId();
                long StorageLocationSize = selectBookingStorageLocationSize(liftIds, LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_RESERVED);
                if (StorageLocationSize == 1){
                    //电梯已被预约，判断结束楼层是否一致
                    //查询储位表
                    //LiftStorageLocationEntity liftStorageLocationEntity = getLiftStorageLocation(liftIds,  LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE);
    //                QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
    //                liftStorageLocationEntityQueryWrapper.eq("lift_id", liftIds);
    //                liftStorageLocationEntityQueryWrapper.eq("booking", LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_RESERVED);
    //                LiftStorageLocationEntity liftStorageLocationEntity = liftStorageLocationMapper.selectOne(liftStorageLocationEntityQueryWrapper);
                    QueryWrapper<LiftAgvcTaskEntity> liftAgvcTaskEntityQueryWrapper = new QueryWrapper<>();
                    liftAgvcTaskEntityQueryWrapper.eq("lift_id",liftIds);
                    liftAgvcTaskEntityQueryWrapper.eq("booking",LiftTaskStateConstant.LIFT_STORAGE_LOCATION_RESERVED);
                    LiftAgvcTaskEntity liftAgvcTaskEntity = liftAgvcTaskMapper.selectOne(liftAgvcTaskEntityQueryWrapper);
                    //判断起始结束楼层是否一致
                    Integer liftStartFloor = liftAgvcTaskEntity.getLiftStartFloor();
                    Integer liftEndFloor = liftAgvcTaskEntity.getLiftEndFloor();
                    //结束楼层一致
                    if (Objects.equals(startFloor, liftStartFloor) && Objects.equals(endFloor, liftEndFloor)){
                        //查询储位表，第二个储位
                        String storageLocationId = fetchStorageLocation(liftIds,LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE,LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_LOWEST_PRIORITY);
                        //TODO 分配储位(不创建储位表，当呼叫了电梯再创建，记录保存在任务表中，字段booking，表示被预约)
                        //updateStorageLocation(LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_RESERVED, storageLocationId);
                        //创建任务
                        insertLiftAgvTask(liftIds, agvId, storageLocationId, currentPort, startFloor, endFloor,LiftTaskStateConstant.LIFT_ASSIGNMENT_STORAGE_LOCATION,LiftTaskStateConstant.LIFT_STORAGE_LOCATION_RESERVED);
                        vo(assignmentLiftStorageLocationVo, liftIds, storageLocationId);
                        return assignmentLiftStorageLocationVo;
                    }
                }else if (StorageLocationSize == 0){

                    //比较二台工作电梯的结束楼层谁离的更近，进行预约
                    List<String> liftIdList = liftExecute.stream().map(LiftEntity::getLiftId).collect(Collectors.toList());
                    QueryWrapper<LiftAgvcTaskEntity> liftAgvTaskEntityQueryWrapper = new QueryWrapper<>();
                    liftAgvTaskEntityQueryWrapper.in("lift_id",liftIdList);
                    List<LiftAgvcTaskEntity> liftAgvcTaskEntities = liftAgvcTaskMapper.selectList(liftAgvTaskEntityQueryWrapper);
                    String liftId = liftAgvcTaskEntities.stream().min(Comparator.comparing(s -> Math.abs(s.getLiftEndFloor() - endFloor))).map(LiftAgvcTaskEntity::getLiftId).orElse(null);


                    //电梯未被预约
                    //查询储位
                    String storageLocationId = fetchStorageLocation(liftId, LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE, LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_HIGH_PRIORITY);
                    //TODO 分配储位(不创建储位表，当呼叫了电梯再创建，记录保存在任务表中，字段booking，表示被预约)
    //                updateStorageLocation(LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_RESERVED, storageLocationId);
                    //创建任务
                    insertLiftAgvTask(liftId, agvId, storageLocationId, currentPort, startFloor, endFloor,LiftTaskStateConstant.LIFT_ASSIGNMENT_STORAGE_LOCATION,LiftTaskStateConstant.LIFT_STORAGE_LOCATION_RESERVED);
                    //返回
                    vo(assignmentLiftStorageLocationVo, liftId, storageLocationId);
                    return assignmentLiftStorageLocationVo;
                }
            }
        }
        assignmentLiftStorageLocationVo.setReturnCode(-1);
        assignmentLiftStorageLocationVo.setLiftCode("电梯未分配到储位，等待下次申请");
        return assignmentLiftStorageLocationVo;
    }

    private static void ifUpdateSucceed(Integer i,String succeed, String fail) {
        if (i > 0){
            log.info(succeed);
        }else {
            throw new RuntimeException(fail);
        }
    }

    private Integer updateLiftStorageLocation(String agvId, String storageLocationId) {
        LiftStorageLocationEntity updateLiftStorageLocation = new LiftStorageLocationEntity();
        updateLiftStorageLocation.setStorageState(LiftStorageLocationConstant.LIFT_STORAGE_LOCATION_UNAVAILABLE);
        updateLiftStorageLocation.setAgvId(agvId);
        QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("storage_location_id", storageLocationId);
        int update = liftStorageLocationMapper.update(updateLiftStorageLocation, liftStorageLocationEntityQueryWrapper);
        return update;
    }

    private Long StorageLocationSize(String liftId, Integer liftStorageLocationUnavailable) {
        QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("lift_id", liftId);
        liftStorageLocationEntityQueryWrapper.eq("storage_state", liftStorageLocationUnavailable);
        Long StorageLocationSize = liftStorageLocationMapper.selectCount(liftStorageLocationEntityQueryWrapper);
        return StorageLocationSize;
    }

    private LiftAgvcTaskEntity selectTaskFllor(String taskAgv, String liftId) {
        QueryWrapper<LiftAgvcTaskEntity> liftAgvcTaskEntityQueryWrapper = new QueryWrapper<>();
        liftAgvcTaskEntityQueryWrapper.eq("agv_id", taskAgv);
        liftAgvcTaskEntityQueryWrapper.eq("lift_id", liftId);
        LiftAgvcTaskEntity liftAgvcTaskEntity = liftAgvcTaskMapper.selectOne(liftAgvcTaskEntityQueryWrapper);
        return liftAgvcTaskEntity;
    }

    private Integer updateListTask(String liftId) {
        LiftEntity lift = new LiftEntity();
        lift.setLiftState(LiftConstant.LIFT_STATE_RECEIVING_TASK);
        QueryWrapper<LiftEntity> entityQueryWrapper = new QueryWrapper<>();
        entityQueryWrapper.eq("lift_id", liftId);
        int update = liftMapper.update(lift, entityQueryWrapper);
        return update;
    }

    /**
     * 获取空闲储位
     * @param liftId
     * @return
     */
    private String fetchStorageLocation(String liftId,Integer storageState,Integer priority) {
        QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("lift_id", liftId);
        liftStorageLocationEntityQueryWrapper.eq("storage_state", storageState);
        liftStorageLocationEntityQueryWrapper.eq("priority", priority);
        LiftStorageLocationEntity liftStorageLocation = liftStorageLocationMapper.selectOne(liftStorageLocationEntityQueryWrapper);
        String storageLocationId = liftStorageLocation.getStorageLocationId();
        return storageLocationId;
    }

    /**
     * 查询空余储位表，返回long
     * @param liftId
     * @param storageState
     * @return
     */
    private long selectBookingStorageLocationSize(String liftId, Integer storageState) {
        QueryWrapper<LiftAgvcTaskEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("lift_id", liftId) ;
        liftStorageLocationEntityQueryWrapper.eq("booking", storageState);
        Long liftStorageLocationSize = liftAgvcTaskMapper.selectCount(liftStorageLocationEntityQueryWrapper);
        return liftStorageLocationSize;
    }

    /**
     * 创建任务
     * @param liftId
     * @param agvId
     * @param storageLocationId
     * @param currentPort
     * @param startFloor
     * @param endFloor
     */
    private void insertLiftAgvTask(String liftId, String agvId, String storageLocationId, String currentPort, Integer startFloor, Integer endFloor,Integer state,Integer booking) {
        LiftAgvcTaskEntity liftAgvTaskEntity = new LiftAgvcTaskEntity();
        liftAgvTaskEntity.setLiftId(liftId);
        liftAgvTaskEntity.setAgvId(agvId);
        liftAgvTaskEntity.setStorageLocationId(storageLocationId);
        liftAgvTaskEntity.setRequestDot(currentPort);
        liftAgvTaskEntity.setLiftStartFloor(startFloor);
        liftAgvTaskEntity.setLiftEndFloor(endFloor);
        liftAgvTaskEntity.setStartTime(new Date());
        liftAgvTaskEntity.setState(state);
        liftAgvTaskEntity.setBooking(booking);
        liftAgvcTaskMapper.insert(liftAgvTaskEntity);
    }

    private static void vo(AssignmentLiftStorageLocationVo assignmentLiftStorageLocationVo, String liftId, String storageLocationId) {
        assignmentLiftStorageLocationVo.setReturnCode(1);
        assignmentLiftStorageLocationVo.setLiftCode(liftId);
        assignmentLiftStorageLocationVo.setStoragePoint(storageLocationId);
    }

    private void updateStorageLocation(Integer booking, String storageLocationId) {
        LiftStorageLocationEntity updateLiftStorageLocation = new LiftStorageLocationEntity();
        QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("storage_location_id", storageLocationId);
        liftStorageLocationMapper.update(updateLiftStorageLocation,liftStorageLocationEntityQueryWrapper);
    }

    private LiftStorageLocationEntity getLiftStorageLocation(String liftId, Integer liftStorageLocationUnavailable) {
        QueryWrapper<LiftStorageLocationEntity> liftStorageLocationEntityQueryWrapper = new QueryWrapper<>();
        liftStorageLocationEntityQueryWrapper.eq("lift_id", liftId);
        liftStorageLocationEntityQueryWrapper.eq("storage_state", liftStorageLocationUnavailable);
        LiftStorageLocationEntity liftStorageLocationEntity = liftStorageLocationMapper.selectOne(liftStorageLocationEntityQueryWrapper);
        return liftStorageLocationEntity;
    }

}
